package com.k.kc.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author k
 */
public class ClassUtils {

    public static List<Field[]> getFieldList(Class clazz){
        List<Field[]> fieldList = new ArrayList<>();
        while (clazz != null){
            fieldList.add(clazz.getDeclaredFields());
            clazz = clazz.getSuperclass();
        }
        return fieldList;
    }

    public static List<Field[]> getFieldList(Object obj){
        Class clazz = obj.getClass();
        return getFieldList(clazz);
    }

    public static Class[] getParamTypeByField(Field field,String methodKeyword){
        return ("get".equals(methodKeyword))? new Class[]{} : new Class[]{field.getType()};
    }

    /**
     * TODO bean setter getter 第二个字母大写生成规则坑 属性名 bFlag 对应set方法是setbFlag,不走寻常路
     * @param field field
     * @param methodKeyword  value: get or set
     * @return str
     */
    private static String getMethodNameByField(Field field,String methodKeyword){
        String fieldName = field.getName();
        String methodName;
        int fieldNameLength = fieldName.length();
        if(fieldNameLength == 1){
            methodName = methodKeyword+fieldName.toUpperCase();
        }else if(fieldNameLength > 1){
            if(Character.isUpperCase(fieldName.charAt(1))){
                methodName = methodKeyword+ fieldName;
            }else {
                methodName = methodKeyword+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
            }
        }else{
            methodName = methodKeyword+ fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
        }
        return methodName;
    }

    /**
     *
     * @param clazz
     * @param methodKeyword get or set
     * @param <T>
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws NoSuchMethodException
     */
    public static <T> List<Method> getMethodList(Class<T> clazz,String methodKeyword) throws IllegalAccessException, InstantiationException {
        List<Method> methodList = new ArrayList<>();
        List<Field[]> allFields = getFieldList(clazz);
        for(Field[] fieldArr:allFields){
            for (Field field : fieldArr) {
                Class[] paramType = getParamTypeByField(field,methodKeyword);
                try {
                    Method method  = clazz.getMethod(getMethodNameByField(field,methodKeyword), paramType);
                    methodList.add(method);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }
        }
        return methodList;
    }


    /**
     * map to bean
     * @param map
     * @param clazz
     * @param <T>
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     */
    public static <T> T mapToBean(Map<String,Object> map , Class<T> clazz) throws IllegalAccessException, InstantiationException, InvocationTargetException {
        List<Field[]> allFields = getFieldList(clazz);
        T obj = clazz.newInstance();
        for(Field[] fieldArr:allFields){
            for (Field field : fieldArr) {
                System.out.println("field:"+field.getName() + " fieldType:"+field.getType());
                Class[] paramType = getParamTypeByField(field,"set");
                try {
                    Method setMethod = clazz.getMethod(getMethodNameByField(field,"set"), paramType);
                    Object value = map.get(field.getName());
                    if(value != null ){
                        setMethod.invoke(obj,value);
                    }
                } catch (NoSuchMethodException e) {
                    System.out.println(field.getName()+ " set value error");
                    e.printStackTrace();
                }
            }
        }
        return obj;
    }

    public static void main(String[] args) {
        Map<String,Object> map = new HashMap<>();
        map.put("byteField",(byte)1); // ok with (byte)1
        map.put("shortField",(short)2); // ok with (short)2
        map.put("intField",3); // ok
        map.put("longField",4); // ok
        map.put("floatField",5.1F); // ok with 5.1F
        map.put("doubleField",6.1); // ok
        map.put("charField",'a'); // ok
        map.put("booleanField",true);// ok

        map.put("byteWrapField",(byte)1); // ok with (byte)1
        map.put("shortWrapField",(short)2); // ok with (short)2
        map.put("intWrapField",3); // ok
        map.put("longWrapField",4L); // OK with L
        map.put("floatWrapField",5.1f); // ok with f
        map.put("doubleWrapField",6.1); //ok
        map.put("charWrapField",'a'); // ok
        map.put("booleanWrapField",true); //ok

        map.put("stringField","xiaoMing");
        try {
            User u = ClassUtils.mapToBean(map,User.class);
            System.out.println(u);
        } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
            e.printStackTrace();
        }
    }

}
